package in.co.codedoc.sql;

import in.co.codedoc.cg.annotations.DBType;
import in.co.codedoc.ioc.IOCContainer;
import in.co.codedoc.sql.mapping.DoubleDoubleInputColumnMap;
import in.co.codedoc.sql.mapping.IntIntInputColumnMap;
import in.co.codedoc.sql.mapping.LongBigintInputColumnMap;
import in.co.codedoc.sql.mapping.ShortSmallintInputColumnMap;
import in.co.codedoc.sql.mapping.StringCharInputColumnMap;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Set;

public class InsertStatement
	extends SQLStatement
{
	public InsertStatement()
	{
		columns = new ArrayList<TableColumn>();
		columnMaps = new ArrayList<DBInputColumnMap>();
	}

	@Override
	public PrintWriter Generate(PrintWriter out) throws IOException
	{
		out.write("INSERT INTO ");
		
		Set<Table> tables = GatherTables(columns);
		
		if(tables.isEmpty())
		{
			throw new RuntimeException
			(
				"Insert statement should indicate at least one column to be set." +
				" The rare case of ALL columns having default values is not supported." +
				" Contact the framework developer."
			);
		}
		
		out.write(tables.iterator().next().GetTableName());
		
		out.write('(');
		boolean firstColumn = true;
		
		for(TableColumn tc:columns)
		{
			if(!firstColumn)
			{
				out.write(',');
			}
			
			out.write(tc.GetQualifiedName());
			firstColumn = false;
		}
		
		out.write(") VALUES (");
		for(int i = 0; i< columns.size();i++)
		{
			if(i>0)
			{
				out.write(',');
			}
			
			out.write('?');
		}
		out.write(')');
		
		return out;
	}

	@Override
	public void GetParameterValues(LinkedList<DBInputColumnMap> values)
	{
		values.addAll(columnMaps);
	}

	public InsertStatement Set(TableColumn tc,IDBase v)
	{
		return Set(tc,new LongBigintInputColumnMap(v.GetId()));
	}

	public InsertStatement Set(TableColumn tc,String v)
	{
		return Set(tc,new StringCharInputColumnMap(v));
	}
	
	public InsertStatement Set(TableColumn tc,short v)
	{
		return Set(tc,new ShortSmallintInputColumnMap(v));
	}
	
	public InsertStatement Set(TableColumn tc,int v)
	{
		return Set(tc,new IntIntInputColumnMap(v));
	}
	
	public InsertStatement Set(TableColumn tc,long v)
	{
		return Set(tc,new LongBigintInputColumnMap(v));
	}
	
	public InsertStatement Set(TableColumn tc,double v)
	{
		return Set(tc,new DoubleDoubleInputColumnMap(v));
	}

	public InsertStatement Set(TableColumn tc,Object v,DBType dbType)
	{
		return Set(tc,IOCContainer.Lookup(DBMapUtil.class).GetInputColumnMap(v, dbType));
	}
		
	public InsertStatement Set(TableColumn tc)
	{
		columns.add(tc);
		columnMaps.add(new DummyInputColumnMap());
		return this;
	}
	
	public InsertStatement Bind(TableColumn tc,IDBase v)
	{
		return Bind(tc,new LongBigintInputColumnMap(v.GetId()));
	}

	public InsertStatement Bind(TableColumn tc,String v)
	{
		return Bind(tc,new StringCharInputColumnMap(v));
	}
	
	public InsertStatement Bind(TableColumn tc,short v)
	{
		return Bind(tc,new ShortSmallintInputColumnMap(v));
	}
	
	public InsertStatement Bind(TableColumn tc,int v)
	{
		return Bind(tc,new IntIntInputColumnMap(v));
	}
	
	public InsertStatement Bind(TableColumn tc,long v)
	{
		return Bind(tc,new LongBigintInputColumnMap(v));
	}
	
	public InsertStatement Bind(TableColumn tc,double v)
	{
		return Bind(tc,new DoubleDoubleInputColumnMap(v));
	}

	public InsertStatement Bind(TableColumn tc,Object v,DBType dbType)
	{
		return Bind(tc,IOCContainer.Lookup(DBMapUtil.class).GetInputColumnMap(v, dbType));
	}
	
	public InsertStatement Bind(TableColumn tc,DBInputColumnMap columnMap)
	{
		int index = -1;
		for(int i = 0;(index == -1)&& (i < columns.size());i++)
		{
			if(columns.get(i).equals(tc))
			{
				index = i;
			}
		}
		
		if(index == -1)
		{
			throw new RuntimeException
			(
				"Failed to bind value for " + tc +
				". Column not found in the insert statement."
			);
		}
		else
		{
			columnMaps.set(index, columnMap);
		}
		
		return this;
	}

	public InsertStatement Set(TableColumn tc,DBInputColumnMap columnMap)
	{
		columns.add(tc);
		columnMaps.add(columnMap);
		return this;
	}

	private ArrayList<DBInputColumnMap> columnMaps;
	private ArrayList<TableColumn> columns;
}
